package ch.zhaw.swp1.model.message;

import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Observable;

import ch.zhaw.swp1.controller.MessageHandler;
import ch.zhaw.swp1.model.message.address.Address;
import ch.zhaw.swp1.model.message.body.Body;
import ch.zhaw.swp1.model.message.validator.Validator;
import ch.zhaw.swp1.model.status.Status;
import ch.zhaw.swp1.model.status.error.MessageRecipientError;

/**
 * Generic class representing a message.
 * @author Daniel Tiefenauer
 */
public abstract class Message extends Observable implements Validator, Cloneable, Serializable {

	/**
	 * Default version
	 */
	private static final long serialVersionUID = 1L;
	protected Address sender = null;
	protected List<Address> recipients = new ArrayList<Address>();
	private Body body = null;	
	private Date sendDate = null;
	protected List<Status> statusList = new ArrayList<Status>();

	/**
	 * Send message (dummy logic)
	 */
	public void send(){
		SimpleDateFormat df = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss");
		String date = df.format(new Date(Calendar.getInstance().getTimeInMillis()));
		if (sendDate != null){
			date = df.format(sendDate);
		}
		
		
		System.out.println("==============================================");
		System.out.println("Sending message of type: " + getMessageType());
		System.out.println("Sender: \t" + sender.getAddress());
		System.out.println("Recipients: \t" + getRecipientsAsString(", "));
		System.out.println("Message Text: \t" + getBody());
		System.out.println("Send Time: \t" + date);		
		System.out.println("==============================================");
	}
	
	/**
	 * Process message (validate, update status list and notify observers)
	 */
	public void process(){
		validate();
		if(statusList.isEmpty()){
			MessageHandler.addMessage(this);
		}
		// STDOUT for testing purposes
		else{
			for(Status status : statusList){
				System.out.println(status);
			}
		}
		setChanged();
		notifyObservers(statusList);
	}

	/************************************************************************************/
	/* START Validation
	/*----------------------------------------------------------------------------------*/
	/**
	 * Validate a message. This is the generic method to validate a message.
	 * All subclasses must use this method via super.validate().
	 * @return a list of status objects describing any errors (empty, if message is ok)
	 */
	public List<Status> validate(){
		statusList.clear();
		validateRecipients();
		validateSender();
		validateBody();
		return statusList;
	}

	/**
	 * Validate all recipients
	 */
	protected void validateRecipients() {
		// Check if recipient is specified
		if(recipients.isEmpty()){
			statusList.add(new MessageRecipientError());
		}
		
		// Iteration over recipients
		Iterator<Address> it = recipients.iterator();
		while (it.hasNext()){
			// iteration over status list of each recipient
			// stati are added to message status list
			List<Status> recipientStatusList = it.next().validate();
			Iterator<Status> st = recipientStatusList.iterator();
			while(st.hasNext()){
				statusList.add(st.next());
			}
		}
	}
	
	/**
	 * Validate sender
	 */
	protected void validateSender(){
		List<Status> senderStatusList = sender.validate();
		Iterator<Status> it = senderStatusList.iterator();
		while(it.hasNext()){
			// iteration over status list of sender
			// stati are added to message status list
			statusList.add(it.next());
		}
	}
	
	/**
	 * Validate message body
	 */
	public void validateBody(){
		List<Status> bodyStatusList = body.validate();
		Iterator<Status> it = bodyStatusList.iterator();
		while(it.hasNext()){
			// iteration over status list of body
			// stati are added to message status list
			statusList.add(it.next());
		}
	}
	
	/**
	 * Check if the message is valid and ready to be sent
	 * @return true, if the message is valid, false if not
	 */
	public boolean isValid(){
		return statusList.isEmpty();
	}

	/**
	 * Get list of currently stored stati
	 * @return a list of stati
	 */
	public List<Status> getStatusList() {
		return statusList;
	}
	/*----------------------------------------------------------------------------------*/
	/* END Validation
	/************************************************************************************/
	
	/************************************************************************************/
	/* Methods
	/************************************************************************************/
	/**
	 * Add new recipient to list of recipients
	 * @param recipient a String containing the address of the recipient to be set
	 */
	public abstract void addRecipient(String recipient);

	/**
	 * Add new recipient to list of recipients
	 * @param recipient the recipient to add
	 */
	public final void addRecipient(Address recipient) {
		recipients.add(recipient);
	}
	
	/**
	 * Add a list of recipients to existing list
	 * @param list a list of recipients
	 */
	public final void addRecipientList(List<Address> list){
		Iterator<Address> it = list.iterator();
		while(it.hasNext()){
			recipients.add(it.next());
		}
	}
	
	/**
	 * Get a text representation of the message for printout
	 */
	public final String toString() {
		StringBuffer sb = new StringBuffer();
		sb.append("Recipients:\n" + getRecipientsAsString("; ") + "\n");
		sb.append("Sender:\n" + this.sender.toString() + "\n");
		sb.append("Body:\n" + this.body.toString());
		return sb.toString();
	}
	
	/**
	 * Return list of recipients as string
	 * @param delim delimiter between recipients
	 * @return a String containing the recipients separated by delimiter
	 */
	private String getRecipientsAsString(String delim){
		String recipientsString = "";
		Iterator<Address> it = recipients.iterator();
		while(it.hasNext()){
			recipientsString += it.next().getAddress() + delim;
		}
		return recipientsString;
	}
	
	/************************************************************************************/
	/* START Setters/Getters
	/*----------------------------------------------------------------------------------*/
	public abstract void setSender(String sender);
	public abstract MessageType getMessageType();
	public final List<Address> getRecipients() {
		return recipients;
	}
	public final void setRecipients(List<Address> recipients) {
		this.recipients = recipients;
	}
	public final Address getSender() {
		return sender;
	}
	public final void setSender(Address sender) {
		this.sender = sender;
	}
	public final Body getBody() {
		return body;
	}
	public final void setBody(Body body) {
		this.body = body;
	}
	public Date getSendDate(){
		return sendDate;
	}
	public void setSendDate(Date date){
		sendDate = date;
	}
	public long getSendTime(){
		if (sendDate != null){
			Calendar cal = Calendar.getInstance();
			cal.setTime(sendDate);
			return cal.getTimeInMillis();
		}
		return 0;
	}
	public void setSendTime(long newTime){
		sendDate = new Date(newTime);
	}
	/************************************************************************************/
	/* END Setters/Getters
	/************************************************************************************/

}
